package com.wzb.mybatisplus.bitaction.electrumaction.two;

import org.bouncycastle.jce.ECNamedCurveTable;
import org.bouncycastle.jce.spec.ECNamedCurveParameterSpec;
import org.bouncycastle.math.ec.ECPoint;

import java.math.BigInteger;
import java.util.Arrays;

public class BitcoinMainNetPublicKeyDecoder {

    /**
     * 从压缩公钥解压出完整的 (x, y) 坐标
     * @param compressedPubKey 压缩公钥（66个十六进制字符，以02或03开头）
     * @return 包含x和y坐标的BigInteger数组 [x, y]
     */
    public static BigInteger[] decompressPublicKey(String compressedPubKey) {
        try {
            // 验证输入格式
            validateCompressedPublicKey(compressedPubKey);

            // 获取secp256k1曲线参数（比特币主网使用）
            ECNamedCurveParameterSpec spec = ECNamedCurveTable.getParameterSpec("secp256k1");

            // 将十六进制字符串转换为字节数组
            byte[] pubKeyBytes = hexStringToByteArray(compressedPubKey);

            // 从字节数组解码点（Bouncy Castle自动处理压缩格式）
            ECPoint point = spec.getCurve().decodePoint(pubKeyBytes);

            // 获取归一化坐标
            ECPoint normalized = point.normalize();

            BigInteger x = normalized.getAffineXCoord().toBigInteger();
            BigInteger y = normalized.getAffineYCoord().toBigInteger();

            // 验证点是否在曲线上
            if (!isPointOnCurve(x, y, spec)) {
                throw new IllegalArgumentException("解压出的点不在secp256k1曲线上");
            }

            return new BigInteger[]{x, y};

        } catch (Exception e) {
            throw new RuntimeException("解压公钥失败: " + e.getMessage(), e);
        }
    }

    /**
     * 验证压缩公钥格式
     */
    private static void validateCompressedPublicKey(String pubKey) {
        if (pubKey == null || pubKey.length() != 66) {
            throw new IllegalArgumentException("压缩公钥必须是66个十六进制字符");
        }

        String prefix = pubKey.substring(0, 2);
        if (!"02".equals(prefix) && !"03".equals(prefix)) {
            throw new IllegalArgumentException("压缩公钥必须以02或03开头");
        }

        // 验证是否为有效的十六进制字符串
        if (!pubKey.matches("^[0-9A-Fa-f]{66}$")) {
            throw new IllegalArgumentException("公钥包含无效的十六进制字符");
        }
    }

    /**
     * 验证点是否在secp256k1曲线上
     */
    private static boolean isPointOnCurve(BigInteger x, BigInteger y, ECNamedCurveParameterSpec spec) {
        try {
            // 曲线方程: y² = x³ + 7 mod p
            BigInteger left = y.pow(2).mod(spec.getCurve().getField().getCharacteristic());
            BigInteger right = x.pow(3)
                    .add(BigInteger.valueOf(7))
                    .mod(spec.getCurve().getField().getCharacteristic());

            return left.equals(right);
        } catch (Exception e) {
            return false;
        }
    }

    /**
     * 将坐标重新压缩为公钥格式（用于验证）
     */
    public static String compressToPublicKey(BigInteger x, BigInteger y) {
        ECNamedCurveParameterSpec spec = ECNamedCurveTable.getParameterSpec("secp256k1");
        ECPoint point = spec.getCurve().createPoint(x, y);
        byte[] compressed = point.getEncoded(true);
        return byteArrayToHexString(compressed).toUpperCase();
    }

    /**
     * 从坐标生成未压缩公钥
     */
    public static String createUncompressedPublicKey(BigInteger x, BigInteger y) {
        ECNamedCurveParameterSpec spec = ECNamedCurveTable.getParameterSpec("secp256k1");
        ECPoint point = spec.getCurve().createPoint(x, y);
        byte[] uncompressed = point.getEncoded(false);
        return byteArrayToHexString(uncompressed).toUpperCase();
    }

    // 工具方法
    public static byte[] hexStringToByteArray(String s) {
        int len = s.length();
        byte[] data = new byte[len / 2];
        for (int i = 0; i < len; i += 2) {
            data[i / 2] = (byte) ((Character.digit(s.charAt(i), 16) << 4)
                    + Character.digit(s.charAt(i+1), 16));
        }
        return data;
    }

    public static String byteArrayToHexString(byte[] bytes) {
        StringBuilder sb = new StringBuilder();
        for (byte b : bytes) {
            sb.append(String.format("%02x", b));
        }
        return sb.toString();
    }

    /**
     * 完整的演示示例
     */
    public static void main(String[] args) {
        // 比特币主网真实的压缩公钥示例
        String[] mainNetCompressedKeys = {
                "KxSTXgLr1abCUrw4o17zXY8PvG4YqwUST582krGuX4oSczGTKADD",
                "1HLoD9E4SDFFPDiYfNYnkBLQ85Y51J3Zb1"
        };

        for (String compressedKey : mainNetCompressedKeys) {
            try {
                System.out.println("=== 处理压缩公钥 ===");
                System.out.println("压缩公钥: " + compressedKey);

                // 解压获取坐标
                BigInteger[] coordinates = decompressPublicKey(compressedKey);
                BigInteger x = coordinates[0];
                BigInteger y = coordinates[1];

                System.out.println("X坐标: " + x.toString(16).toUpperCase());
                System.out.println("Y坐标: " + y.toString(16).toUpperCase());

                // 验证：重新压缩应该得到原始公钥
                String recompressed = compressToPublicKey(x, y);
                System.out.println("重新压缩: " + recompressed);
                System.out.println("压缩验证: " + recompressed.equals(compressedKey));

                // 生成未压缩格式
                String uncompressed = createUncompressedPublicKey(x, y);
                System.out.println("未压缩公钥: " + uncompressed);
                System.out.println("未压缩格式验证: " + uncompressed.startsWith("04"));

                System.out.println();

            } catch (Exception e) {
                System.err.println("处理公钥失败: " + compressedKey);
                System.err.println("错误: " + e.getMessage());
            }
        }
    }



}